Conv2dTransposeFusion
计算二维转置卷积(也可视为对 Conv2D 输入求梯度的等价形式,常称反卷积,但并非矩阵意义下的严格逆运算)。 input_x、out_y 为 NHWC;权重布局为 (C_in, K_h, K_w, C_out/G)(不是空间 NHWC 特征图)。
输入 shape 为 \((N, H_{in}, W_{in}, C_{in})\),其中:
\(N\) 为 batch size
\(C_{in}\) 为输入通道数
\(H_{in}, W_{in}\) 为特征图高度与宽度
权重 shape 为 \((C_{in}, K_h, K_w, C_{out}/G)\),其中 \(G\) 为 group_。 输出 shape 为 \((N, H_{out}, W_{out}, C_{out})\)。
对输入位置 \((i_h, i_w)\)、核偏移 \((k_h, k_w)\),映射到输出坐标:
当 \((o_h, o_w)\) 落在输出有效范围内时累加,否则该项为 0。 对组 \(g\)、输入通道 \(c_i\)、输出通道 \(c_o\),令 \(C_i' = C_{in}/G\),\(C_o' = C_{out}/G\),并记 \(c_i' = g \cdot C_i' + c_i\),\(c_o' = g \cdot C_o' + c_o\):
其中 \(s_h/s_w\) 为步长,\(d_h/d_w\) 为膨胀系数。实现上先对输入与转换后的权重做分块矩阵乘,再经 col2im 按上式写回输出;调用前通常需将输出清零。
- 输入:
input_x - 输入数据地址(NHWC)
input_w - 卷积核权重地址,形状
[C_in, K_h, K_w, C_out/G]bias - 偏置地址
conv_param - ConvParameter 结构体地址,字段见下
core_mask - 核掩码
ConvParameter 定义:
1typedef struct ConvParameter {
2 void* workspace_; // 必填。布局见下方「警告」;*_s 与 *_p 容量算法不同
3 int output_batch_; // 输出 batch
4 int input_batch_; // 输入 batch
5 int input_h_; // 输入高
6 int input_w_; // 输入宽
7 int output_h_; // 输出高
8 int output_w_; // 输出宽
9 int input_channel_; // 输入通道
10 int output_channel_; // 输出通道
11 int kernel_h_; // 卷积核高
12 int kernel_w_; // 卷积核宽
13 int group_; // 组数
14 int pad_l_; // 左填充
15 int pad_u_; // 上填充
16 int dilation_h_; // 高方向膨胀
17 int dilation_w_; // 宽方向膨胀
18 int stride_h_; // 高方向步长
19 int stride_w_; // 宽方向步长
20 int buffer_size_; // 仅 *_p 使用(元素个数,常用 1024);*_s 不读此字段。见下方警告。
21} ConvParameter;
- 输出:
out_y - 输出地址(NHWC);调用前通常先清零
- 支持平台:
FT78NEMT7004
警告
MT7004的workspace_ / buffer_size_ 分配算法:
共享版 _s 仍然需要 workspace_,且不能只按 packed 权重大小申请。 共享核会把转换后的权重写到 workspace_ 开头,随后在同一块 DDR workspace 上按 core_id × 2048 写每核 matmul 输出 scratch; 输入/权重 tile 在 AM,不占 buffer_size_。 若只分配 weight_elems,多核写入 scratch 会越界(典型表现:DSP 复位)。
共享版 _s 不读 buffer_size_。汇编/C 参考把共享路径的 scratch 步长 写死为 2048 元素;把 buffer_size_ 改成 1024 不会缩小 _s 所需 workspace,也不应据此少分 core_num × 2048。
buffer_size_ 只约束私有版 _p:权重后的分块 tile 预算(元素个数, 测试常用 1024)。FP 私有再加 1 × buffer_size_,HP 私有再加 3 × buffer_size_(输入 tile / 权重 tile / matmul 输出)。
workspace_ 统一布局:[packed 权重] + [权重后的 scratch]; packed 权重元素数 weight_elems = C_in × K_h × K_w × (C_out / G)。 推荐分配(字节 = 元素数 × sizeof(float),按实际 dtype 调整):
weight_elems = C_in * K_h * K_w * (C_out / G); /* *_s / DDR 共享:不看 buffer_size_;硬编码每核 2048 */ workspace_elems = weight_elems + core_num * 2048; /* fp_*_p:看 buffer_size_(常用 1024) */ workspace_elems = weight_elems + buffer_size_; /* hp_*_p */ workspace_elems = weight_elems + 3 * buffer_size_;
备注
FT78NE 支持 int8、fp32
MT7004 支持 fp16、fp32
共享存储版本:
-
void i8_conv2d_transpose_s(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param, int core_mask)
-
void hp_conv2d_transpose_s(float16 *input_x, float16 *input_w, float16 *out_y, float16 *bias, ConvParameter *conv_param, int core_mask)
-
void fp_conv2d_transpose_s(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param, int core_mask)
C调用示例:
1// MT7004 示例(共享存储多核,DDR 地址)
2void TestConvTransposeSMCFp32(int *input_shape, int *weight_shape, int *output_shape,
3 int *stride, int *padding, int *dilation, int groups,
4 float *bias, int core_mask) {
5 int core_id = get_core_id();
6 int logic_core_id = GetLogicCoreId(core_mask, core_id);
7 int core_num = GetCoreNum(core_mask);
8 float *input_data = (float *)0x88000000;
9 float *weight = (float *)0x89000000;
10 float *output_data = (float *)0x90000000;
11 float *bias_data = (float *)0x91000000;
12 ConvParameter *param = (ConvParameter *)0x92000000;
13 /* *_s 大坑:workspace_bytes = (weight_elems + core_num * 2048) * sizeof(float)
14 * 只按 packed 权重大小申请会越界;与 buffer_size_ 无关(*_s 不读该字段)。 */
15 float *workspace = (float *)0xA0000000;
16 if (logic_core_id == 0) {
17 memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
18 memset(output_data, 0,
19 output_shape[0] * output_shape[1] * output_shape[2] * output_shape[3] * sizeof(float));
20 param->dilation_h_ = dilation[0];
21 param->dilation_w_ = dilation[1];
22 param->group_ = groups;
23 param->input_batch_ = input_shape[0];
24 param->input_h_ = input_shape[1];
25 param->input_w_ = input_shape[2];
26 param->input_channel_ = input_shape[3];
27 param->kernel_h_ = weight_shape[1];
28 param->kernel_w_ = weight_shape[2];
29 param->output_batch_ = output_shape[0];
30 param->output_h_ = output_shape[1];
31 param->output_w_ = output_shape[2];
32 param->output_channel_ = output_shape[3];
33 param->stride_h_ = stride[0];
34 param->stride_w_ = stride[1];
35 param->pad_u_ = padding[0];
36 param->pad_l_ = padding[2];
37 param->workspace_ = workspace;
38 /* *_s 不读 buffer_size_;填 0 即可,勿按此字段缩小 workspace */
39 param->buffer_size_ = 0;
40 }
41 sys_bar(0, core_num);
42 fp_conv2d_transpose_s(input_data, weight, output_data, bias_data, param, core_mask);
43}
44
45void main() {
46 int in_channel = 4;
47 int out_channel = 4;
48 int groups = 2;
49 int input_shape[4] = {4, 4, 4, in_channel}; // NHWC
50 int weight_shape[4] = {in_channel, 3, 3, out_channel / groups};
51 int output_shape[4] = {4, 6, 6, out_channel}; // NHWC
52 int stride[2] = {1, 1};
53 int padding[4] = {0, 0, 0, 0};
54 int dilation[2] = {1, 1};
55 float bias[] = {1.0f, 2.0f, 3.0f, 4.0f};
56 int core_mask = 0b1111;
57 TestConvTransposeSMCFp32(input_shape, weight_shape, output_shape,
58 stride, padding, dilation, groups, bias, core_mask);
59}
私有存储版本:
-
void i8_conv2d_transpose_p(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param, int core_mask)
-
void hp_conv2d_transpose_p(float16 *input_x, float16 *input_w, float16 *out_y, float16 *bias, ConvParameter *conv_param, int core_mask)
-
void fp_conv2d_transpose_p(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param, int core_mask)
C调用示例:
1// MT7004 示例(私有存储单核,AM 地址)
2void TestConvTransposeL2Fp32(int *input_shape, int *weight_shape, int *output_shape,
3 int *stride, int *padding, int *dilation, int groups,
4 float *bias, int core_mask) {
5 float *input_data = (float *)0x10000000;
6 float *output_data = (float *)0x10018000;
7 float *weight = (float *)0x10048000;
8 float *bias_data = (float *)0x1004C000;
9 ConvParameter *param = (ConvParameter *)0x10050000;
10 /* *_p:workspace_bytes = (weight_elems + buffer_size_) * sizeof(float);HP 则 +3*buffer_size_ */
11 float *workspace = (float *)0x10054000;
12 memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
13 memset(output_data, 0,
14 output_shape[0] * output_shape[1] * output_shape[2] * output_shape[3] * sizeof(float));
15 param->dilation_h_ = dilation[0];
16 param->dilation_w_ = dilation[1];
17 param->group_ = groups;
18 param->input_batch_ = input_shape[0];
19 param->input_h_ = input_shape[1];
20 param->input_w_ = input_shape[2];
21 param->input_channel_ = input_shape[3];
22 param->kernel_h_ = weight_shape[1];
23 param->kernel_w_ = weight_shape[2];
24 param->output_batch_ = output_shape[0];
25 param->output_h_ = output_shape[1];
26 param->output_w_ = output_shape[2];
27 param->output_channel_ = output_shape[3];
28 param->stride_h_ = stride[0];
29 param->stride_w_ = stride[1];
30 param->pad_u_ = padding[0];
31 param->pad_l_ = padding[2];
32 param->workspace_ = workspace;
33 param->buffer_size_ = 1024; /* 元素个数;仅 *_p 使用 */
34 fp_conv2d_transpose_p(input_data, weight, output_data, bias_data, param, core_mask);
35}
36
37void main() {
38 int in_channel = 4;
39 int out_channel = 4;
40 int groups = 2;
41 int input_shape[4] = {4, 4, 4, in_channel}; // NHWC
42 int weight_shape[4] = {in_channel, 3, 3, out_channel / groups};
43 int output_shape[4] = {4, 6, 6, out_channel}; // NHWC
44 int stride[2] = {1, 1};
45 int padding[4] = {0, 0, 0, 0};
46 int dilation[2] = {1, 1};
47 float bias[] = {1.0f, 2.0f, 3.0f, 4.0f};
48 int core_mask = 0b0001;
49 TestConvTransposeL2Fp32(input_shape, weight_shape, output_shape,
50 stride, padding, dilation, groups, bias, core_mask);
51}